算力提升117%,资源使用下降50%,打开集群优化正确姿势
导读:美图日益增长的庞大数据和计算任务,对大数据集群的计算能力、存储能力、稳定性、扩展性等都提出了较大的挑战。目前美图技术团队针对大数据集群做了系列的优化,通过对计算引擎进行改造而达到算力的提升,通过对集群的不断优化提升稳定性的同时规范集群使用。在大数据集群优化的实践中,我们也总结了一些实践经验,也期待和大家有更多的探讨。
集群概述
如今大数据在各行业的应用越来越广泛,运营基于数据跟踪运营效果,产品基于数据分析提升转化率,开发基于数据衡量系统优化效果等。美图公司有美拍、美图秀秀、美颜相机等多个app,部分app 会基于大数据做个性化推荐、搜索、报表分析、反作弊、广告等,整体对大数据的业务需求比较多、应用也比较广泛。促使大数据集群需要支持广泛的业务线及应用,驱动这些业务增长,让数据的使用更为高效。
在这里主要想和大家分享下我们两大集群Hadoop&Spark 集群的优化与演进的一些具体经验与尝试。Hadoop 集群主要是用于离线报表计算, Spark 集群主要用于用户个性化推荐、反作弊相关等。当前有 PB 级的历史总量,每天的增量也在近百TB ,每天近万个计算任务,内部有比较多的业务线,并且各业务线比较广泛地运用到数据。这些日益增长的庞大数据和计算任务,充分考验大数据集群的计算能力、存储能力、稳定性、扩展性等。在早期的时候我们有遇到如下问题:稳定性差、组件瓶颈、算力低、组件bug 多、集群成本高、业务方使用缺乏规范、以及数据安全的问题。
针对以上业务背景、技术挑战以及历史问题,促使我们需要对大数据集群不断的迭代优化,以应对各种挑战。我们先对计算引擎进行改造以达到提升算力的目标,其次通过集群的优化在稳定性的提升的同时规范了集群的使用,但是仍然存在不满足当前的算力情况,所以我们正在自研ad-hoc 系统,以及通过采集任务画像信息去匹配任务的最佳参数和最佳引擎。
首先我们看一下现在的技术框架,如图一:
图一目前的技术架构
我们将此技术架构分为三层:
1. 数据产品层:有美图大数据统计平台、dataface 数据可视化平台、机器学习等上层应用,主要用于连接用户与服务,降低数据使用门槛,展现数据价值。这里重点说一下数据工坊,数据工坊是大数据部门自主研发的集数据采集、导入导出、离线计算、实时计算等功能于一体的数据开放平台。业务方有任何数据使用的需求都可以在数据工坊上自助完成。
2. 引擎计算层:提供计算服务,包括交互式查询、即席查询、离线任务计算、数据导入导出、数据分析等服务。由于美图业务的特殊性以及多样性,我们无法通过一个统一的工具去cover 所有业务场景,于是我们引入了这些最适合自身业务的组件。
3. 基础服务层:负责数据的存储、元数据的存储以及任务的计算环境支持。
在数据产品层和引擎计算层间,我们也做了相应的组件作为连接数据产品层和引擎计算层的桥梁:
1. 分布式调度系统:用户管理作业间依赖关系,根据用户任务配置定时调度作业管理的工具;
2. DataWork-client:支持执行 SQL 以及 SQL file (包含权限校验、 SQL 校验),将执行完的结果数据导出到集群上实现 Hadoop&Spark 集群数据互通,并支持上传文件到 hdfs ,支持多种文件格式导出,任务结束邮件通知用户,日志输出等级自定义。接入方式可以是 java-sdk 和Shell 客户端
引擎改造
我们为什么需要对引擎做改造?首先随着公司业务快速的增长,受限于Hive 引擎本身运行机制的低效,频繁的磁盘 IO ,网络请求都会成为计算瓶颈,使得Hive 对任务的实效性不能得到很好的保证。考虑到 Hive引擎已经无法满足当前需求,所以我们对引擎进行了改造,以实现更高效的计算引擎。
其次在资源使用上其对内存依赖较小,不太适合我们当前128G内存的机型,从而导致资源不能合理地被利用。
基于上面几个原因,我们开始着手对引擎做相应改造。我们将Hive 引擎升级为了 Hive on Spark 引擎。选择Hive on Spark 引擎主要基于以下几点考量:
1. 语法上能支持标准的Hive SQL;
2. 性能上能给业务带来较大的提升;
3. 迁移成本上对业务方上是透明,没有任何迁移成本。
将Hive 升级至 Hive on Spark ,我们也做了详细的规划和预演,确保在保证业务的情况下,能顺利的完成升级。在整个的升级过程中,主要有六大步:
1. 第一步:由于Hive on Spark 对版本之前的匹配度是极其敏感,若没有选择到最好的版本可能会带来较大的影响,例如:性能达不到预期、对 SQL 语句的兼容性达不到最佳状态、较多的未知风险等。于是团队开始调研选择最佳的Hive与Spark 的版本组合,整个过程中我们对 10 多个Hive 和 Spark 版本进行组合,最后发现 Hive-2.3.3 on Spark-2.2.2 是一个适合当前业务的最佳版本组合;
2. 第二步:选定版本之后开始校验日常的这些任务,确保对日常的任务能做到百分百能兼容,保证对业务无损;
3. 第三步:升级Hive 版本,这里有一个背景由于我们选定的是 Hive-2.3.3 ,但是当前的版本是Hive-2.1.1 ,于是要先对Hive 做了一次升级,我们从 Hive 官网上下载 Hive-2.3.3 ,并将我们内部做的组件改造合并到高版本中,Hive 的升级本质上就是 HiveMetastore 的升级,期间也做了较多的升级预演,保证升级成功率;
4. 第四步:部署Spark 环境,由于 Spark 环境部署相对简单,只需要将依赖的 jar 上传到 hdfs ,并通过配置文件将依赖关联上即可,大家可以理解为 Spark 环境其实就是一个客户端,此时Hive-2.3.3 与Spark-2.2.2 还未联系上,组件是独立的;
5. 第五步:引擎改造算是一个重大升级,一旦失败会导致整个集群不可用,影响面非常大,于是我们做了大量的升级预演以及升级脚本,制定了多个问题发生时的降级方案确保升级过程中遇到风险属于一个可控状态;
6. 第六步:在所有准备工作都已完成后就开始正式升级,升级过程相对顺利未出现较大的问题。
此次升级由于我们最终是既要能保证性能能得到提升,也要保证对业务是无损状态,所以我们做了非常多的升级预演,风险降级方案等等,其中也涉及到多个部门的协同合作。具体改造流程如图二所示。
图二改造流程
从灰度将任务迁移至Hive on Spark 引擎到全部升级改造的完成,随后我们也进行了升级盘点计算收益。Hive on Spark 引擎改造上线后,效果显著,整体效果如图三所示(绿色表示高使用率,黄色表示中使用率,红色表示低使用率)。整体来看,优化前集群资源使用率高,有些天近乎打满了,优化后集群资源的使用率下降明显。经计算,任务性能提升117.8%,计算资源下降40%~50%,任务时效提前2~3h(任务时效指的是业务获得结果数据的时间)。
图三引擎改造后效果图对比
在对计算力提升后,我们认为资源使用上还可以有较大的节约,于是进行集群资源动态管理的升级,那为什么要对资源进行动态管理呢?
一个任务需要多个Task 来运行,每个 Task 需要占用一定的资源来执行任务,而由于每个 Task 处理的数据量不一样,导致最终各个 Task 完成任务的时间和所用的资源不一样,在原生的资源管理模式下,需要等到任务完全运行完所有 Task 才释放资源,这就造成一部分 Task 提前完成自己的任务但还占用着资源,造成资源的浪费。
动态资源是一种对资源进行动态管理的模式,首先它可以根据任务大小来动态申请资源数,避免资源的过度申请。其次,它可以对提前完成任务的Task 的那部分资源进行回收用于其它任务,这就避免了资源的极大浪费,也降低了业务方的使用门槛。动态资源上线后,经实际测算,平均每个任务资源节省 71.2% 。
Hive 查询优化
随后我们对hive 的查询速度又做了一次优化,重构了 Metastore ,解决 hive 慢查询问题。
图四请求流程图
这里先说一下整个请求流程(图四),客户端发起任务请求,请求会发送到 HiveServer , HiveServer 通过解析任务后将请求发送给 HiveMetastore 生成 SQL 语句向 MySql 查询源数据信息,并将源数据信息返回给 HiveMetastore 。 HiveMetastore 底层会生成一个性能极低的 SQL 去查询 MySql 表是否将源数据信息返回给 HiveMetastore ,问题就出现在这个性能极低的 SQL 语句上。使用过 MySql 的同学应该知道MySql 表的查询在千万级别的时候就显得很有压力,MySql 在到达千万级别的查询时表的索引和分区要格外注意,由于美图各个业务的数据量比较大,同一个业务形成的 Hive 分区多,此时 MySql 服务压力过大, CPU 跑满,响应时间过长,影响其它任务。因此我们对 HiveMetastore 生成的慢查询进行优化。
这里说一下这个优化的背景,在2018 年5 月份时候,发生一次19 个重型任务集中发起,CPU 直接打满的情况,当时采用的临时措施是将任务错峰执行,减轻CPU 负载,为我们排查问题争取时间。但是随后我们发现是平台频繁请求MetaStore ,造成响应延迟,引发雪崩。当时优先想到优化代码减少与 MetaStore 的交互,但是这还是没有从根本上解决问题, CPU 负载情况虽然得到缓解,但仍然还在一个不可接受的范围内。
后面经仔细排查,发现根本原因是由于19 个重型任务,同时执行3500W 无索引的SQL 请求,才导致 MySql 源数据查询迟缓,于是对症下药,对进行任务解析,重组 SQL 利用索引。优化后响应时间从原先的 26s 降低到现在的1s 。
经过对HiveMetaStore 的优化,整体效果如下图五(在做过改造后 MySql 服务 CPU 负载的尖刺已经完全消失,稳定性得到了极大的提升)。优化前CPU 的使用情况很高,尖刺多,优化后整体下降且平缓。经统计,升级后整体任务编译性能提升 69.8% ,这些重型任务编译时间提升92.5% ,稳定性大幅度提升。而由于在编译这些重型任务会影响其它任务的编译时间,所以当这些重型任务的编译问题解决后,整体的编译性能也得到提升。
图五HiveMetaStore 的优化效果图
集群演进
在对引擎进行改造之后,任务的算力得到大幅度提升,使用反馈也越来越好。我们开始着手于整个集群其它方面的改进。
集群资源管理队列的改造
首先我们进行了集群资源管理队列的改造。集群初建期,集群整体利用率低,大数据集群 Yarn 使用的队列调度方式为 Capacity Schedule ,这种方式有一个弊端,就是在不能实现资源共享,资源空闲的队列不能和在等待运行的其它队列的任务共享资源。举个例子,就像你去银行办理业务,普通用户只能在普通窗口取号排队办理业务,若VIP 窗口没有业务办理,此时你并不能去 VIP 窗口办理业务,这样的设置就不是很适合当前美图业务,而且对资源也造成了较大浪费。而随着任务数量增长,经常发现任务在 Yarn 中排队等待获取资源的情况,然而可能只是该队列资源被打满,整个集群中还有空闲的资源未被分配。正是由于 Capacity Schedule 这种调度模式,导致集群资源没有被充分利用,集群利用率低下,任务队列使用不合理。
为了更好的解决上面提到的问题,于是我们决定将Capacity 调整至 Fair 队列调度方式。 Fair 可以实现资源的共享,更合理高效的利用集群资源,减少任务排队问题,更适合当前业务场景。整体效果图如图六。
图六将Capacity 调整至 Fair 队列调度方式效果图
在升级了Fair 队列调度方式后,集群在资源的利用率和任务堆积情况有了很大的改善,在资源利用率方面,由于闲置的资源能马上被调用,使得在升级后资源得到了极大的利用,资源利用率明显升高。从任务堆积情况来看,升级前任务经常出现大量堆积,曲线尖刺多,升级后,任务堆积数量明显下降,曲线区域平缓。整体效果见图七。
图七任务堆积情况优化前后对比图
存储优化
随后我们又从数据本身着手,进一步优化存储和资源收益,我们发现在数据的存储格式上还可以有较大的性能提升。我们的初始集群表数据基本是AVRO 或者TextFile 格式的,这种格式在 Hive on Spark 上性能方面表现较差,而且存储上占用大量磁盘空间,并且在查询时速度较慢,于是我们在基于现有的Hive on Spark 引擎上,调研多种存储格式的查询性能,如图八。
图八存储格式的查询性能对比
通过调研比对,可以清晰的看出,parquet 格式和 orc 格式在总体性能比较时表现最为突出,不过我们团队最终选择了 orc 格式,大家也许会好奇为什么我们选定了 orc 格式而不选取 parquet 。
parquet 是由Twitter 和Cloudera 合作开发,在纯spark 引擎上用的较多,orc 是 Hive 官方团队自己研发,对hive 而言兼容性好于parquet ,所以在两者性能相差不大的情况下我们选择了Hive 兼容性较好的 orc 格式。
在对所有表的存储改造后,我们统计了一下收益,改造后底层表存储空间释放了约1058TB*3,执行效率(主要指任务执行时间)提升42.41%,乘3的原因是HDFS 存储系统的数据存储机制是一份数据有三个副本,以保证数据可恢复性,提升数据安全,所以计算存储大小要乘3。在中间表方面,存储空间释放约1381TB*3,计算资源节约15%,执行效率提升8.11%。为什么中间表执行效率提升没底层表大?这是由于中间表已经是对底层表的进一步改造了,所以执行效率提升较不明显,这也在我们预期之中。总体来看,列式存储改造后在存储、执行效率和资源方面都有很好的改善。
随着集群数据的日益增长,慢慢的小文件也越来越多,小文件问题引起了我们的重视,为什么要重视小文件增长呢?因为小文件增多会对NameNode 内存造成压力,这里的 NameNode 可以理解为集群的管理者的角色,堆内存使用过高会导致 NameNode 挂掉,同时文件数量的增加 Block 的数量也会增加, NameNode 的启动加载、主备切换时间都会变长,升级风险高,并且会影响HDFS 文件的读写请求响应以及大量 NameNode 查元数据时间等。所以在数据量大到一定的规模时,大量数据的增长使得我们需要遏制小文件的增长。
我们将小于10M 的文件定义为小文件,通过对集群的统计来看,集群的小文件占比达到了86.4% ,小文件增长速度快、占比高、可优化空间大,于是,团队决定自研小文件自动合并工具,同时进行清理冷数据,做历史数据归档压缩等遏制集群小文件增长,以减少 NameNode 压力,提升服务稳定性。
我们在2018 年12 月底统计的小文件数量达到2.3 亿多,经优化后,2019 年3 月份小文件数量减少到 9 千多万,小文件共减少 57.5% ,当然,小文件的处理工作我们一直在持续进行中。
集群安全管理
在对集群的算力、存储、资源及稳定性上进行优化之后,我们开始着手于集群的安全管理,大数据集群最基本的就是数据,是一个公司的宝贵财富。我们需要将它们很好管理起来,将相应的数据和资源开放给对应的用户使用,防止被窃取、被破坏等,如何有效的管理数据?这涉及到大数据安全管理。
为了更好的对访问数据的权限进行管理,我们上线了Ranger , Ranger是 Apache 开源软件,可以对用户组、数据库、表、字段等进行细粒度的权限控制,更好的保障数据安全。
同时我们上线了机架感知,何为要做机架感知?为何可以提高安全性?我们知道,集群的一台台机器都是放在一个个机架上的,Hadoop 在设计时考虑到数据的安全与高效,数据文件默认在 HDFS 上存放三份,没有启动机架感知时, HDFS 随机写入机器,如果三份数据都写在同一个机架上则这份数据安全性不高。此前也发生过机房一个机架出现了冷冻液渗漏问题,导致一整个机架上的服务器全部坏损的问题。这个问题引起了我们的警惕,鉴于此,我们上线了机架感知,数据文件的备份落在不同机架上,避免由于数据落在同个机架上而机架出现故障导致数据丢失损坏。
同时机架感知可以提高数据的读写速度,数据的读写在相同机架内的机器相互间访问更快,不同机架则需要跨机架传输。没有启动机架感知时,数据的随机读写会导致大量跨机架传输,在job 处理的数据量非常的大,这种情况会造成机架传输之间的网络流量成倍的上升,成为性能的瓶颈。机架感知存储副本机制不同,它会首先在离client 最近的节点写入第一份数据,第二份数据取其它不同机架进行写入,第三份数据取与第一份数据相同机架的节点写入。这样,节省了跨机架传输次数及流量,提高了读写性能。为了验证机架感知功能对 Hadoop 集群读写性能影响程度,我们在机架感知上线前先对 Hadoop 集群读写性能进行测试,再上线后又进行一次读写性能进行测试。不同的时间段机架感知上线后性能提升不同,平均下来,读性能提升了 7.5%,写性能提升了 34.1%。
在使用大数据开源组件的过程中,难免会遇到组件Bug 。当出现组件 Bug 时,我们第一时间就要去修复它,避免影响大数据线上服务。另外,针对现实业务场景,不可避免要对某些特殊业务进行支持,这时候就需要根据业务定制修改组件逻辑,以满足业务要求。在大数据集群持续演进的这段时间,我们不断对集群组件做修复、定制和优化,以维护集群稳定支撑业务发展,修复各类组件源码共计70 多处,主要有以下部分:Spark 源码修复 12 处,Hive 源码修复 34 处,Hadoop 源码修复 6 处,Ranger源码修复 10 处,Flink源码修复 6 处,Presto源码修复 6 处,除此之外内部自研的组件包括:
1)hdfs 小文件优化组件;
2)分布式调度系统;
3)datawork-client ;
4)bitmap 组件;
5)引擎转发系统 (孵化中);
6)集群统一管理平台(孵化中)。
集群的规范使用
在集群的管理过程中我们也一直加强着集群的规范使用,内部自研的有数据工坊平台。数据工坊是大数据部门自主研发集数据采集、导入导出、离线计算、实时计算等功能于一体的数据开放平台,如下图九。业务方有任何数据使用的需求都可以在数据工坊上自助完成,能够实现集群统一入口管理。
图九数据工坊架构图
工坊可以通过设置定时任务,调用分布式调度系统将任务传给Hive SDK (Hive SDK 是我们自研的连接工坊与Hive 组件的工具), Hive SDK 将任务根据需求打到HiveMetaStore 、 HiveServer 、 Ranger 等,通过语法校验(检验 SQL 语法准确性)、提交数量校验、权限校验、分区、时间校验、敏感参数拦截、算子复杂度校验、表名、字段名解析等一系列的任务校验后将任务提交到 Yarn 上面执行,执行完后将结果数据写入 Hdfs ,而工坊此时可以到 Yarn 或者 Hdfs 上拉取任务运行资源做计费管理和结果数据等,并将结果数据返回给用户。
工坊的存在使得用户不需直接操作集群,而是直接工坊配置即可,由工坊进行统一的入口管理,使得集群使用更加规范化。
图十数据工坊处理定时任务流程图
后期规划
目前集群上大多数任务还是依赖于离线计算,查询效率上还存在很大的提升空间,未来我们可以继续由分钟级别提升到秒级别。此外,此前因为缺少多维分析平台,只能做定制化的统计,无法满足灵活分析。因此,目前我们正在自研AD-Hoc & OLAP 平台,用以提升数据分析自由度,提升大数据集群计算能力,完善大数据体系。
此外我们通过技术手段还可以对任务做更精细化的配置,这个也是后续我们规划的重点。通过采集任务的画像信息匹配执行过程中的最佳引擎与最佳参数以实现对任务更为精细化的配置,其中任务画像信息可以是:使用到的算子、join个数、union个数、输入数据量、container实际使用内存、shuffle读写数据量等等。
图十一后期任务流程图
整体后期任务流程图如图十一所示,用户通过前端发起查询请求,再通过服务后台请求我们的组件SDK 与路由转发 Router 组件,SDK 通过验证任务的合法性并为任务带上最佳参数,通过 Router 组件为任务匹配最佳的引擎。例如:适合离线集群的会转发到离线集群,或者转发到Ad-Hoc 引擎或 OLAP引 擎,为加速查询我们在上层做了一次数据缓存层如 Hdfs&Alluxio ,若未命中缓存则同样将任务匹配好最佳参数并转发到相应的引擎上执行。
参考阅读:
技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。转载请注明来自高可用架构「ArchNotes」微信公众号及包含以下二维码。
高可用架构
改变互联网的构建方式
长按二维码 关注「高可用架构」公众号